package com.dongbao.action;

import cn.hutool.system.SystemUtil;
import com.dongbao.common.AuthorityEnum;
import com.dongbao.entity.CallMethod;
import com.google.common.collect.Lists;
import com.intellij.openapi.actionSystem.AnAction;
import com.intellij.openapi.actionSystem.AnActionEvent;
import com.intellij.openapi.actionSystem.LangDataKeys;
import com.intellij.openapi.diagnostic.Logger;
import com.intellij.openapi.fileChooser.FileChooser;
import com.intellij.openapi.fileChooser.FileChooserDescriptor;
import com.intellij.openapi.fileChooser.FileChooserDescriptorFactory;
import com.intellij.openapi.project.Project;
import com.intellij.openapi.util.text.StringUtil;
import com.intellij.openapi.vfs.VirtualFile;
import com.intellij.psi.*;
import com.intellij.psi.impl.source.PsiMethodImpl;
import com.intellij.psi.impl.source.tree.PsiCommentImpl;
import com.intellij.psi.impl.source.tree.java.*;
import com.intellij.psi.util.PsiTypesUtil;
import com.intellij.ui.UIBundle;
import lombok.extern.slf4j.Slf4j;
import org.jetbrains.annotations.NotNull;
import org.xmind.core.*;

import java.io.IOException;
import java.util.ArrayList;
import java.util.List;
import java.util.Optional;
import java.util.concurrent.atomic.AtomicReference;

@Slf4j
public class GenerateFlowAction extends AnAction {

    /**
     * 文件分隔符
     */
    public static final String FILE_SEPARATOR = SystemUtil.getOsInfo().getFileSeparator();

    private static final Logger LOG = Logger.getInstance(GenerateFlowAction.class);

    @Override
    public void actionPerformed(@NotNull AnActionEvent e) {
        Project project = e.getData(LangDataKeys.PROJECT);
        if (project == null) {
            return;
        }

        PsiMethodImpl curMethod = (PsiMethodImpl)e.getData(LangDataKeys.PASTE_TARGET_PSI_ELEMENT);
        String topic = methodAnnoValue(curMethod);
        IWorkbook iWorkbook = generWorkbook(topic);
        ITopic rootTopic = iWorkbook.getPrimarySheet().getRootTopic();
        /* 调用方法的解析方法进行去解析方法内部的信息 */
        ArrayList<ITopic> chapterTopics = analysisMethod(iWorkbook,curMethod);
        for(ITopic chindTopic:chapterTopics){
            rootTopic.add(chindTopic);
        }

        String savePath = selectSavePath(project);
        // 保存
        try {
            iWorkbook.save(savePath + "\\" + FILE_SEPARATOR + topic + ".xmind");
        } catch (IOException ex) {
            ex.printStackTrace();
        } catch (CoreException ex) {
            ex.printStackTrace();
        }
        /* 获取方法体内的注释信息 */

    }

    public String selectSavePath(Project project){
        AtomicReference<String> savePath = new AtomicReference<>();
        final FileChooserDescriptor dirChooser = FileChooserDescriptorFactory.createSingleFolderDescriptor();
        dirChooser.setShowFileSystemRoots(true);
        dirChooser.setHideIgnored(true);
        dirChooser.setTitle("选择思维导图保存路径");
        FileChooser.chooseFiles(dirChooser, project, null, files -> {
            for (VirtualFile file : files) {
                savePath.set(file.getPath());
            }
        });
        return savePath.get();
    }

    /**
     * 解析一个方法中的引入与调用等信息
     * */
    private ArrayList<ITopic> analysisMethod(IWorkbook workbook,PsiMethod method){
        LOG.info("当前解析方法:"+method.getName());
        ArrayList<ITopic> chapterTopics = Lists.newArrayList();
        PsiElement[] children = method.getBody().getChildren();
        if(children!=null && children.length!=0){
            for(PsiElement element:children){
                chindPsiRow(workbook,method,element,chapterTopics);
            }
        }
        return chapterTopics;
    }

    /**
     * 子节点代码行解析
     * */
    private void chindPsiRow(IWorkbook workbook,PsiMethod method,PsiElement element,List<ITopic> chapterTopics){
        if(element.getText().indexOf("context.getFillBlankRowsMap()")!=-1){
            LOG.info("566");
        }
        if(element instanceof PsiCommentImpl){
            /* 注释信息 */
            if(element.getText().indexOf("FLOW:")!=-1){
                PsiCommentImpl comment=(PsiCommentImpl)element;
                ITopic topic = workbook.createTopic();
                String commentText = comment.getText().substring(comment.getText().indexOf("FLOW:") + 5, comment.getText().length());
                if(StringUtil.isNotEmpty(commentText)){
                    if(StringUtil.isEmpty(commentText)){
                        LOG.info("666");
                    }
                    topic.setTitleText(commentText);
                    chapterTopics.add(topic);
                }
            }
        }else if(element instanceof PsiReturnStatementImpl){
            /* return 返回 */
            PsiReturnStatementImpl returnStatement=(PsiReturnStatementImpl)element;
            /* return中存在的方法调用 */
            LOG.info("未完待续");
        }else if(element instanceof PsiDeclarationStatementImpl){
            /* 表达式调用 */
            PsiDeclarationStatementImpl statement=(PsiDeclarationStatementImpl)element;
            /* 返回调用的方法Mothed */
            CallMethod callMethod = getCallMethod(statement,method);
            if(callMethod==null || Optional.ofNullable(callMethod.getMethod()).orElse(null)==null){
                return;
            }
            String annoValue = methodAnnoValue(callMethod.getMethod());
            if(annoValue==null){
                /* 当前表达式所调用的方法顶部没有注解 */
                return ;
            }
            ITopic topic = workbook.createTopic();
            if(StringUtil.isEmpty(annoValue)){
                LOG.info("666");
            }
            topic.setTitleText(annoValue);
            ArrayList<ITopic> iTopics = analysisMethod(workbook, callMethod.getMethod());
            for(ITopic iTopic:iTopics){
                topic.add(iTopic);
            }
            chapterTopics.add(topic);
        }else if(element instanceof PsiTryStatementImpl){
            /* 表达式调用 */
            PsiTryStatementImpl tryStatement=(PsiTryStatementImpl)element;
            PsiElement[] children = tryStatement.getChildren();
            for(PsiElement childrenElement:children){
                chindPsiRow(workbook,method,childrenElement,chapterTopics);
            }
        }else if(element instanceof PsiIfStatementImpl){
            /* 表达式调用 */
            PsiIfStatementImpl tryStatement=(PsiIfStatementImpl)element;
            PsiElement[] children = tryStatement.getChildren();
            for(PsiElement childrenElement:children){
                chindPsiRow(workbook,method,childrenElement,chapterTopics);
            }
        }else if(element instanceof PsiExpressionStatementImpl){
            PsiExpressionStatementImpl expressionStatement = (PsiExpressionStatementImpl)element;
            PsiElement[] expElements = expressionStatement.getChildren();
            if(expressionStatement.getText().indexOf("log.")!=-1 || expressionStatement.getText().indexOf("LOG.")!=-1 ){
                /* 对日志记录的表达式不进行处理 */
                return;
            }
            for(PsiElement expElement:expElements){
                /* 拆分节点去解析内部是否存在方法调用的 */
                chindPsiRow(workbook,method,expElement,chapterTopics);
            }
        }else if(element instanceof PsiMethodCallExpressionImpl){
            if(element.getText().equals("params.containsKey(\"isCache\")")){

            }
            CallMethod statementMethodCall = getStatementMethodCall(element,method);
            ITopic topic = workbook.createTopic();
            Boolean result = anlyReferClassMethod(workbook, topic, statementMethodCall);
            if(result){
                chapterTopics.add(topic);
            }
        }else if(element instanceof PsiBlockStatementImpl){
            PsiBlockStatementImpl boockStatem = (PsiBlockStatementImpl)element;
            PsiElement[] children = boockStatem.getChildren()[0].getChildren();
            /* 去遍历一遍大括号中的一些语句 */
            for(PsiElement childrenElement:children){
                chindPsiRow(workbook,method,childrenElement,chapterTopics);
            }
        }else if(element instanceof PsiAssignmentExpressionImpl){
            PsiAssignmentExpressionImpl assignmentExpr = (PsiAssignmentExpressionImpl)element;
            PsiElement[] children = element.getChildren();
            for(PsiElement childrenElement:children){
                chindPsiRow(workbook,method,childrenElement,chapterTopics);
            }
            LOG.info("6666");
        }else if(element instanceof PsiMethodCallExpressionImpl){
            CallMethod statementMethodCall = getStatementMethodCall(element,method);
            ITopic topic = workbook.createTopic();
            Boolean result = anlyReferClassMethod(workbook, topic, statementMethodCall);
            if(result){
                chapterTopics.add(topic);
            }
        }else{
            if(element.getChildren().length!=0){
                PsiElement[] children = element.getChildren();
                for(PsiElement childrenElement:children){
                    chindPsiRow(workbook,method,childrenElement,chapterTopics);
                }
            }
        }
    }

    /**
     * 从方法的顶部注解获取注解中的Value值
     * */
    private String methodAnnoValue(PsiMethod method){
        String topic = null;
        PsiAnnotation[] annotations = method.getAnnotations();
        for(PsiAnnotation annotation:annotations){
            PsiAnnotationImpl annotationImpl = (PsiAnnotationImpl)annotation;
            if(AuthorityEnum.MAIN_ANNOTIONS_NAME.equals(annotationImpl.getQualifiedName()) || AuthorityEnum.CHILD_ANNOTIONS_NAME.equals(annotationImpl.getQualifiedName())){
                topic = annotationImpl.findAttributeValue("value").getText().replace("\"","");
                return topic;
            }
        }
        return topic;
    }

    /**
     * 传入类 与 方法的对象去获取里面的注解与注释信息
     * */
    private Boolean anlyReferClassMethod(IWorkbook workbook,ITopic topic,CallMethod callMethod){
        /* 获取方法中的每个节点 */
        PsiMethod method = callMethod.getMethod();
        if(method==null){
            return false;
        }
        String title = methodAnnoValue(method);
        if(StringUtil.isEmpty(title)){
            return false;
        }
        topic.setTitleText(title);
        PsiElement[] methodChildren = method.getBody().getChildren();
        for(PsiElement element:methodChildren){
            List<ITopic> topics = new ArrayList<>();
            chindPsiRow(workbook,method,element,topics);
            if(!topics.isEmpty()){
                for(ITopic curTopic:topics){
                    topic.add(curTopic);
                }
            }
//            if(element instanceof PsiTryStatementImpl){
//                /* try cache 语句 */
//                /* 表达式调用 */
//                PsiTryStatementImpl tryStatement=(PsiTryStatementImpl)element;
//                PsiElement[] children = tryStatement.getChildren();
//                ArrayList<ITopic> chapterTopics = Lists.newArrayList();
//                for(PsiElement childrenElement:children){
//                    chindPsiRow(workbook,method,childrenElement,chapterTopics);
//                }
//                for(ITopic topicItem:chapterTopics){
//                    topic.add(topicItem);
//                }
//            }else if(element instanceof PsiIfStatementImpl){
//
//                if(element.getText().indexOf("重新组合报表配置对象")!=-1){
//                    LOG.info(element.getText());
//                }
////                chindPsiRow
//                /* 表达式调用 */
//                PsiIfStatementImpl tryStatement=(PsiIfStatementImpl)element;
//                PsiElement[] children = tryStatement.getChildren();
//                ArrayList<ITopic> chapterTopics = Lists.newArrayList();
//                for(PsiElement childrenElement:children){
//                    chindPsiRow(workbook,method,childrenElement,chapterTopics);
//                }
//                for(ITopic topicItem:chapterTopics){
//                    topic.add(topicItem);
//                }
//            }else if(element instanceof PsiReturnStatementImpl){
////                chindPsiRow
//                /* 表达式调用 */
//                PsiReturnStatementImpl returnStatement=(PsiReturnStatementImpl)element;
//                PsiElement[] children = returnStatement.getChildren();
//                ArrayList<ITopic> chapterTopics = Lists.newArrayList();
//                for(PsiElement childrenElement:children){
//                    chindPsiRow(workbook,method,childrenElement,chapterTopics);
//                }
//                for(ITopic topicItem:chapterTopics){
//                    topic.add(topicItem);
//                }
//            }else if(element instanceof PsiCommentImpl){
//                /* 注释信息 */
//                if(element.getText().indexOf("FLOW:")!=-1){
//                    PsiCommentImpl comment=(PsiCommentImpl)element;
//                    ITopic childTopic = workbook.createTopic();
//                    String commentText = comment.getText().substring(comment.getText().indexOf("FLOW:") + 5, comment.getText().length());
//                    if(StringUtil.isNotEmpty(commentText)){
//                        childTopic.setTitleText(commentText);
//                        topic.add(childTopic);
//                    }
//                }
//            }
        }
        return true;
    }

    /**
     * 解析表达式 返回这个表达式所调用的方法
     * */
    private CallMethod getCallMethod(PsiDeclarationStatementImpl statement,PsiMethod method){
        PsiElement[] childrens = statement.getChildren();
        for(PsiElement element:childrens){
            CallMethod callMethod = getStatementMethodCall(element,method);
            if(callMethod!=null){
                return callMethod;
            }
        }
        return null;
    }

    /**
     * 获取到当前的传入对象字节点的方法调用的方法
     * */
    private CallMethod getStatementMethodCall(PsiElement element,PsiMethod method){
        CallMethod callMethod=new CallMethod();
        if(element instanceof PsiMethodCallExpressionImpl){
            PsiMethodCallExpressionImpl methodCallExpressionImpl = (PsiMethodCallExpressionImpl)element;
            callMethod.setMothodCall(methodCallExpressionImpl);
            /* 从方法调用的表达式中获取到对应调用的类和方法 */
            analysisClassAndMethod(callMethod,methodCallExpressionImpl,method);
            return callMethod;
        }else if(element.getChildren().length!=0){
            for(PsiElement childElement:element.getChildren()){
                CallMethod childCallMethod = getStatementMethodCall(childElement,method);
                if(childCallMethod!=null){
                    return childCallMethod;
                }
            }
        }else {
            return null;
        }
        return null;
    }

    /**
     * 从方法调用的表达式中解析出调用的类与所调用的方法
     * */
    private void analysisClassAndMethod(CallMethod callMethod,PsiMethodCallExpressionImpl methodCallExpressionImpl,PsiMethod method){
        PsiElement[] childrens = methodCallExpressionImpl.getChildren();
        for(PsiElement element:childrens){
            if(element instanceof PsiReferenceExpressionImpl){
                PsiReferenceExpressionImpl referenceExpression = (PsiReferenceExpressionImpl)element;
                if(referenceExpression.getFirstChild() instanceof PsiReferenceExpressionImpl){
                    PsiReferenceExpressionImpl firstChild = (PsiReferenceExpressionImpl)referenceExpression.getFirstChild();
                    PsiIdentifierImpl lastChild = (PsiIdentifierImpl)referenceExpression.getLastChild();
                    PsiClass containingClass = method.getContainingClass();
                    PsiField field = method.getContainingClass().findFieldByName(firstChild.getQualifiedName(), false);
                    if(field==null){
                        /* 在当前类的全局变量声明中未找到这个变量,查找是否为系统变量 */
                        callMethod = null;
                        return ;
                    }else{
                        /* 获取类 */
                        PsiClass psiClass = PsiTypesUtil.getPsiClass(field.getType());
                        callMethod.setPsiClass(psiClass);
                        /* 获取方法 */
                        PsiMethod[] findMethod = psiClass.findMethodsByName(lastChild.getText(), false);
                        /* 根据参数去判断是调用的哪个方法 */
                        if(findMethod.length!=0){
                            callMethod.setMethod(findMethod[0]);
                        }
                    }
                }else if(referenceExpression.getFirstChild() instanceof PsiReferenceParameterListImpl){
                    /* 到当前的类中去搜索这个方法 */
                    PsiClass containingClass = method.getContainingClass();
                    callMethod.setPsiClass(containingClass);
                    PsiMethod[] methodsByName = containingClass.findMethodsByName(referenceExpression.getText(), false);
                    if(methodsByName.length!=0){
                        callMethod.setMethod(methodsByName[0]);
                    }
                }
                break;
            }else{

            }
        }
    }

    /**
     * 创建一个生成 思维导图的对象
     * */
    private IWorkbook generWorkbook(String topic){
        /* 定义一个流程图的对象 */
        // 创建思维导图的工作空间
        IWorkbookBuilder workbookBuilder = Core.getWorkbookBuilder();
        IWorkbook workbook = workbookBuilder.createWorkbook();

        // 获得默认sheet
        ISheet primarySheet = workbook.getPrimarySheet();

        // 获得根主题
        ITopic rootTopic = primarySheet.getRootTopic();
        // 设置根主题的标题
        rootTopic.setTitleText(topic);
        // 章节 topic 的列表
        //ArrayList<ITopic> chapterTopics = Lists.newArrayList();
        // 把章节节点添加到要节点上
        //chapterTopics.forEach(it -> rootTopic.add(it, ITopic.ATTACHED));
        return workbook;
    }


    @Override
    public void update(@NotNull final AnActionEvent e) {
        try{
            PsiElement data2 = e.getData(LangDataKeys.PASTE_TARGET_PSI_ELEMENT);
            if(data2 instanceof PsiMethodImpl){
                PsiMethodImpl curMethod = (PsiMethodImpl)data2;
                PsiAnnotation[] annotations = curMethod.getAnnotations();
                /* 判断是否有生成流程图的注解 */
                Boolean enable = false;
                for(PsiAnnotation annotation:annotations){
                    PsiAnnotationImpl annotationImpl = (PsiAnnotationImpl)annotation;
                    if(AuthorityEnum.MAIN_ANNOTIONS_NAME.equals(annotationImpl.getQualifiedName())){
                        /* 是声明的流程图注解 */
                        enable = true;
                        break;
                    }
                }
                e.getPresentation().setVisible(enable);
            }
        }catch (Exception exce){
            e.getPresentation().setEnabled(false);
        }
    }
}
